package pogvue.gui;

import java.util.*;
import java.io.*;

import java.awt.*;
import java.awt.event.*;
import java.awt.print.*;

import javax.swing.*;

import com.jgoodies.forms.layout.CellConstraints;
import com.jgoodies.forms.layout.FormLayout;

import pogvue.datamodel.*;
import pogvue.gui.event.*;
import pogvue.gui.hub.*;
import pogvue.analysis.*;
import pogvue.io.*;
import pogvue.util.*;

public final class AlignmentPanel extends ControlledPanel implements AlignViewportListener,
								     AdjustmentListener,
								     FontChangeListener,
								     MouseListener,
								     MouseMotionListener,
								     Printable {
  public IdCanvas         idPanel;
  public SeqCanvas        seqCanvas;
  public ScalePanel       scalePanel;
  public JScrollBar       hscroll;
  public JScrollBar       vscroll;

  Alignment               align;
  
  private final AlignViewport av;
  
  int offx;
  int offy;
  
  int oldoffx;
  int oldoffy;
  
  int maxoffx;
  int maxoffy;
  
  public AlignmentPanel(Alignment al) {
    AlignViewport av = new AlignViewport(al,true, false, false, true);
    Controller    c  = new Controller();
    
    this.av = av;
    setController(c);

    
    align = (Alignment)av.getAlignment();
    
    componentInit();
  }
  
  public AlignmentPanel(AlignViewport av, Controller c) {
    this.av         = av;
    
    setController(c);
    
    align = (Alignment) av.getAlignment();
    
    componentInit();
    
  }
  
  public AlignViewport getAlignViewport() {
    return av;
  }
  
  public int print(Graphics g,PageFormat pf, int pi) throws PrinterException {
    System.out.println("In print");
    if (pi >=1) {
      return Printable.NO_SUCH_PAGE;
    }
    paint(g);
    return Printable.PAGE_EXISTS;
  }
  
  private void componentInit() {
    
    idPanel    = new IdCanvas  (av,controller);
    seqCanvas  = new SeqCanvas (av,controller);

    scalePanel = new ScalePanel(av,controller);
    
    hscroll = new JScrollBar(Scrollbar.HORIZONTAL);
    vscroll = new JScrollBar(Scrollbar.VERTICAL);
    
    hscroll.addAdjustmentListener(this);
    vscroll.addAdjustmentListener(this);
    
    seqCanvas.addMouseMotionListener(this);
    seqCanvas.addMouseListener(this);
    
    controller.addListener(seqCanvas);
    controller.addListener(this);
    
    // The window is split into two panels left and right
    
    JPanel leftPanel  = new JPanel();
    JPanel rightPanel = new JPanel();
    
    FormLayout leftLayout = new FormLayout("pref",
					   "30px, pref:grow, pref");
    
    FormLayout rightLayout = new FormLayout("pref:grow, pref",
					    "30px, pref:grow, pref");
    
    leftPanel.setLayout(leftLayout);
    rightPanel.setLayout(rightLayout);
    
    CellConstraints cc = new CellConstraints();
    
    leftPanel.add(idPanel,          cc.xy(1,2, "fill, fill"));

    rightPanel.add(scalePanel,      cc.xy(1,1, "fill, default"));
    rightPanel.add(seqCanvas,       cc.xy(1,2, "fill, fill"));
    rightPanel.add(hscroll,         cc.xy(1,3, "fill, default"));
    rightPanel.add(vscroll,         cc.xy(2,2, "default, fill"));
    
    JSplitPane jsp = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,leftPanel,rightPanel);
        
    jsp.setOneTouchExpandable(true);
    jsp.setDividerLocation(idPanel.getPreferredSize().width);
    
    setLayout(new BorderLayout());
    add("Center",jsp);
    setScrollValues(0,0);
  }
  
  public boolean handleAlignViewportEvent(AlignViewportEvent e) {
	
    if (e.getType() != AlignViewportEvent.LIMITS) {
      setScrollValues(av.getStartRes(),av.getStartSeq());
      WindowUtil.invalidateComponents(this);
      repaint();
      //validate();
      //validateTree();
      revalidate();
    }
    revalidate();
    return true;
  }

  public boolean handleFontChangeEvent(FontChangeEvent e) {
    return true;
  }

  private void setScrollValues(int offx, int offy) {
	
    int width;
    int height;
    
    //Brings up error in netscape
    if (seqCanvas.getSize().width > 0) {
      width  = seqCanvas.getSize().width;
      height = seqCanvas.getSize().height;
    } else {
      width  = seqCanvas.getPreferredSize().width;
      height = seqCanvas.getPreferredSize().height;
    }
    
    //Make sure the maxima are right
    if (maxoffx != (align.getWidth())) {
      maxoffx = (align.getWidth());
    }
    if (maxoffy != (align.getHeight())) {
      maxoffy = (align.getHeight());
    }
    
    hscroll.setValues(offx, (int)(width/av.getCharWidth()),0,maxoffx);
    vscroll.setValues(offy,height/av.getCharHeight(),0,maxoffy);
    
    int hpageinc = (int)(0.3 * (av.getEndRes() - av.getStartRes()));
    int vpageinc = (int)(0.3 * (av.getEndSeq() - av.getStartSeq()));
    
    hscroll.setUnitIncrement(hpageinc/10 + 1);
    vscroll.setUnitIncrement(1);
    
    if (hpageinc < 1) {
      hpageinc = 1;
    }
    if (vpageinc < 1) {
      vpageinc = 1;
    }
    
    hscroll.setBlockIncrement(hpageinc);
    vscroll.setBlockIncrement(vpageinc);
  }
  
  public void adjustmentValueChanged(AdjustmentEvent evt) {
    // This gets called when the scrollbars are changed - 
    
    if (evt.getSource() == hscroll) {
      
      int offx = hscroll.getValue();
      
      // This should be n*bases per pixel

      int bpp = (int)(1.0/av.getCharWidth());

      offx = offx - offx%bpp;
      hscroll.setValue(offx);
      if (offx != oldoffx) {
	av.setStartRes(offx);
	av.setEndRes((int)(offx + (seqCanvas.size().width/av.getCharWidth())));
	
	controller.handleAlignViewportEvent(new AlignViewportEvent(this,av,AlignViewportEvent.HSCROLL));
	
	hscroll.revalidate();
      }
      oldoffx = offx;
    }
    
    if (evt.getSource() == vscroll) {
      int offy = vscroll.getValue();
      if (oldoffy != offy) {
	av.setStartSeq(offy);
	av.setEndSeq(offy + seqCanvas.getSize().height/av.getCharHeight());
	controller.handleAlignViewportEvent(new AlignViewportEvent(this,av,AlignViewportEvent.LIMITS));
      }
      oldoffy = offy;
    }
  }
    
  public void mouseEntered(MouseEvent evt) { }
  public void mouseExited (MouseEvent evt) { }
  public void mouseClicked(MouseEvent evt) { }
  public void mouseMoved  (MouseEvent evt) { 
  }
  public void mousePressed(MouseEvent evt) {
  }
  public void mouseDragged(MouseEvent evt) { }
  
  public void mouseReleased(MouseEvent evt) {
	
    int x = evt.getX();
    int y = evt.getY();
    
    int res = (int)(x/av.getCharWidth() + av.getStartRes());
    int seq = av.getIndex(y);
    
    //char resstr = align.getSequenceAt(seq).getSequence().charAt(res);

    
    int pos = align.getSequenceAt(0).findPosition(res);
    

    if (SwingUtilities.isMiddleMouseButton(evt)) {
      System.out.println("We have a meta event!!!");
      int    width     = av.getEndRes() - av.getStartRes() + 1;
      double charwidth = av.getCharWidth();
      int    startres  = res - width/2;
      int    endres    = startres + width;
      
      av.setStartRes(startres);
      av.setEndRes(endres);

      controller.handleAlignViewportEvent(new AlignViewportEvent(this,av,AlignViewportEvent.RESHAPE));	
    }

    // Minipogness - we have the centre so we need to calculate the start and ends
    
    if (av.getMinipog() != null && SwingUtilities.isRightMouseButton(evt)) {
      AlignViewport av2 = av.getMinipog().getAlignViewport();
      
      int    width     = av2.getEndRes() - av2.getStartRes() + 1;
      double charwidth = av2.getCharWidth();
      int    startres  = res - width/2;
      int    endres    = startres + width;
      
      av2.setStartRes(startres);
      av2.setEndRes(endres);
      
      av.getMinipog().getController().handleAlignViewportEvent(new AlignViewportEvent(this,av2,AlignViewportEvent.RESHAPE));

    }


  }
    
  public void reshape(int x, int y, int width, int height) {
    super.reshape(x,y,width,height);
    setScrollValues(av.getStartRes(),av.getStartSeq());
  }
  
  
  public Dimension getPreferredSize() {
    return new Dimension(20,200);
  }
  
  public static void main(String[] args) {
    
    AlignmentI al  = null;
    GFFFile    gff = null;
    GraphFile  grf = null;
    BlatFile   blt = null;

    SequenceI[] s  = null;
    
    try {
      
      // Parse the command line options
      //
      // -seqfile <seqfile> -gfffile <gfffile> -graphfile <graphfile> 
      // -chr <chr> -chrstart <start> -chrend <end> -gapped -dynamic
      
      Hashtable opts = GetOptions.get(args);
      
      String seqfile   = null;
      String gfffile   = null;
      String grffile   = null;
      String bltfile   = null;
      String showseq   = null;
      String chr       = null;
      int    chrstart  = -1;
      int    chrend    = -1;
      int    gapped    = 0;
      
      int    tiernum   = 20;
      
      boolean noseq    = true;

      if (opts.containsKey("-seqfile")) {
	seqfile  = (String)opts.get("-seqfile");
      }
      
      if (opts.containsKey("-showseq")) {
	showseq  = (String)opts.get("-showseq");
      }
      
      if (opts.containsKey("-gfffile")) {
	gfffile  = (String)opts.get("-gfffile");
      }        
      if (opts.containsKey("-graphfile")) {
	grffile  = (String)opts.get("-graphfile");
      }        
      if (opts.containsKey("-blatfile")) {
	bltfile  = (String)opts.get("-blatfile");
      }        
      if (opts.containsKey("-chr")) {
	chr = (String)opts.get("-chr");
      }
      if (opts.containsKey("-start")) {
	chrstart = Integer.parseInt((String)opts.get("-start"));
      }
      if (opts.containsKey("-end")) {
	chrend = Integer.parseInt((String)opts.get("-end"));
      }
      if (opts.containsKey("-gapped")) {
	gapped = 1;
      }
      if (opts.containsKey("-tier")) {
	tiernum = Integer.parseInt((String)opts.get("-tier"));
      }
      if (opts.containsKey("-noseq")) {
	  noseq = true;
      } else {
	  noseq = false;
      }
      
      if (seqfile != null) {
	  if (gapped == 0) {
	      FastaFile f = new FastaFile(seqfile,"File");
	      s  = f.getSeqsAsArray();
	  } else  {
	    GappedFastaFile f = new GappedFastaFile(seqfile,"File");
	    s  = f.getSeqsAsArray();
	  } 
	  al = new Alignment(s);
      } 
	
      if (chr != null && chrstart != -1 && chrend != -1) {
	String fasta_url = AlignViewport.getFastaURL() + "query=" + chr + "&start=" + chrstart + "&end=" + chrend;
	String gff_url   = AlignViewport.getGFFURL()   + "query=" + chr + "&start=" + chrstart + "&end=" + chrend;
	String grf_url   = AlignViewport.getGRFURL()   + "query=" + chr + "&start=" + chrstart + "&end=" + chrend;
	String blt_url   = AlignViewport.getBLTURL()   + "query=" + chr + "&start=" + chrstart + "&end=" + chrend;
	
	System.out.println("GFF " + gff_url);

	String filetype = "URL";
	
	if (opts.containsKey("-zip")) {
	  fasta_url = fasta_url + "&z=1";
	  gff_url   = gff_url + "&z=1";
	  grf_url   = grf_url + "&z=1";
	  blt_url   = blt_url + "&z=1";
	  filetype = "URLZip";
	}
	
	
	if (!opts.containsKey("-dynamic")) {
	    if (noseq == false) {
		GappedFastaFile f = new GappedFastaFile(fasta_url,filetype);
		//FastaFile f = new FastaFile(fasta_url,filetype);
	
		s = f.getSeqsAsArray();	
	    } else {
		StringBuffer tmpseq = new StringBuffer();
		int i = 0;
		while (i < chrend-chrstart+1) {
		    tmpseq.append('X');
		    i++;
		}
		Sequence tmp = new Sequence("Human",tmpseq.toString(),1,chrend-chrstart+1);
		s = new Sequence[1];
		s[0] = tmp;
	    }
	
	    al = new Alignment(s);

	  gff   = new GFFFile(gff_url,filetype);

	  Hashtable   fhash  = gff.getFeatureHash();
	  Enumeration en     = fhash.keys();
          
	  while (en.hasMoreElements()) {
	   String type = (String)en.nextElement();
	   GFF g = (GFF)fhash.get(type);

           if (g.getType().equals("repeat")) {
	       al.addSequence(g);	    
           } else {
	   Vector bumpgff = pogvue.io.GFFFile.bumpGFF(g);

	   for (int i = 0 ; i < bumpgff.size(); i++) {
	       GFF tmpgff = (GFF)bumpgff.elementAt(i);
	       al.addSequence(tmpgff);	    
	   }
           } 
	  }
	  System.out.println("Getting graph features " + grf_url);
	  grf   = new GraphFile(grf_url,filetype);

	  fhash  = grf.getFeatureHash();
	  en     = fhash.keys();
          
	  while (en.hasMoreElements()) {
	  String type = (String)en.nextElement();
	  System.out.println("Got type " + type);

	  //if (type.equals("12merpvalue") || type.equals("omega") || type.indexOf("pi")>=0) {
	  	  GFF ff = new GFF("","",1,2);
	  	  al.addSequence(ff);
	  	  al.addSequence(ff);
		  //}

	  GFF g = (GFF)fhash.get(type);
	  al.addSequence(g);
	  }

	  
	  // Getting blat features
	  System.out.println("Blat url " + blt_url);

	  blt   = new BlatFile(blt_url,filetype);
	  
          Vector featv = blt.getFeatures();

	  GFF g = new GFF("blat","",1,2);

	  if (featv != null) {
	    for (int ii = 0; ii < featv.size(); ii++) {
	      g.addFeature((SequenceFeature)featv.elementAt(ii));
	    }

	    if (featv.size() > 0) {
	      SequenceFeature feat = g.getFeatureAt(0);
	      
	      
	      Vector bumpgff = pogvue.io.GFFFile.bumpGFF(g);
	      
	      for (int i = 0 ; i < bumpgff.size(); i++) {
		
		GFF tmpgff = (GFF)bumpgff.elementAt(i);
		Vector feat2 = tmpgff.getFeatures();
		for (int j = 0; j < feat2.size(); j++) {
		  SequenceFeature sf = (SequenceFeature)feat2.elementAt(j);
		  
		}
		al.addSequence(tmpgff);	    
	      }
	      
	    }
	  }
	  

	} else {
	  Vector orgs = new Vector();
	  orgs.addElement("Human");
	  orgs.addElement("Chimp");
	  orgs.addElement("RheMac");
	  orgs.addElement("MouseLemur");
	  orgs.addElement("Bushbaby");
	  orgs.addElement("Mouse");
	  orgs.addElement("Dog");
	  orgs.addElement("Tenrec");
	  orgs.addElement("Cat");
	  orgs.addElement("Bat");
	  orgs.addElement("Pika");
	  orgs.addElement("Elephant");
	  orgs.addElement("Cavia");
	  orgs.addElement("Rat");
	  orgs.addElement("Squirrel");
	  orgs.addElement("Hedgehog");
	  orgs.addElement("Shrew");
	  orgs.addElement("Armadillo");
	  orgs.addElement("Monodelphis_uber");
	

	  SequenceFragment[] seqarr = new SequenceFragment[orgs.size()];

	  for (int i = 0; i < orgs.size(); i++) {
	    String org = (String)orgs.elementAt(i);
	    SequenceFragment sf = new SequenceFragment(chr,"",chrstart,chrend);
	    sf.setOrg(org);
	    seqarr[i] = sf;
	  }

	  al = new Alignment(seqarr);

	  GFFFragment gfffrag = new GFFFragment(chr,"",chrstart,chrend,"repeat");

	  al.addSequence(gfffrag);
	}

	//if (opts.containsKey("-tier")) {
	//  Vector tiers = gff.getTieredGFFFeatures(tiernum);
	  
	//  for (int i = 0; i < tiers.size(); i++) {
	    
	//    GFF g = (GFF)tiers.elementAt(i);
	//    System.out.println("Adding tier " + ((SequenceFeature)g.getFeatures().elementAt(0)).getType());
	//    al.addSequence(g);
	//  }
      }
      
      // Read the gfffile and add into alignment
      if (gfffile != null) {
	gff   = new GFFFile(gfffile,"File");
	
	if (opts.containsKey("-tier")) {
	  Vector tiers = gff.getTieredGFFFeatures(tiernum);
	  
	  for (int i = 0; i < tiers.size(); i++) {
	    
	    GFFI g = (GFFI)tiers.elementAt(i);
	    System.out.println("Adding tier " + ((SequenceFeature)g.getFeatures().elementAt(0)).getType());
	    al.addSequence(g);
	  }
	} else {
	  Hashtable   fhash  = gff.getFeatureHash();
	  Enumeration en     = fhash.keys();
          
	  while (en.hasMoreElements()) {
	    String type = (String)en.nextElement();
	    GFF g = (GFF)fhash.get(type);
	    System.out.println("Type " + type + " " + g + " " + al);

            if (g.getType().equals("repeat")) {
                   al.addSequence(g);
            } else {
	    Vector bumpgff = pogvue.io.GFFFile.bumpGFF(g);

	    for (int i = 0 ; i < bumpgff.size(); i++) {
		System.out.println("Adding bump " + i);

		GFF tmpgff = (GFF)bumpgff.elementAt(i);
		al.addSequence(tmpgff);	    
	    }
}
	    //al.addSequence(g);
	  }
	}
      }
      // Read the grffile and add into alignment
      if (grffile != null) {
	grf   = new GraphFile(grffile,"File");
	Hashtable   fhash  = grf.getFeatureHash();
	Enumeration en     = fhash.keys();
        
	while (en.hasMoreElements()) {
	  String type = (String)en.nextElement();
	  GFF g = (GFF)fhash.get(type);
	  
	  if (type.equals("12merpvalue") || type.equals("omega") || type.equals("pi")) {
	    al.addSequence(new GFF("","",1,2));
	    al.addSequence(new GFF("","",1,2));
	    al.addSequence(new GFF("","",1,2));	
	    al.addSequence(new GFF("","",1,2));
	  }
	  al.addSequence(g);
	}
      }
      
      if (al != null) {
	AlignSplitPanel jp = GenomeInfoFactory.makePanel(al,"Pogvue",.05,10,null,0,chrstart,chrend,650);
	JFrame          jf = new JFrame("Pogvue");
	

	jp.getAlignmentPanel1().getAlignViewport().setVisibleSequence(true);
	jp.getAlignmentPanel2().getAlignViewport().setVisibleSequence(true);

	jf.setVisible(true);
	jf.getContentPane().add(jp);
	jf.setSize(700,500);

      }
      
    } catch (IOException e) {
      System.out.println("IOException " + e);
    }
  }
}


